package com.xcity.game.adventure;

import java.lang.ref.SoftReference;
import java.security.SecureRandom;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.function.Consumer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.xcity.db.entity.Player;
import com.xcity.db.entity.Relation2;
import com.xcity.db.mapper.PlayerMapper;
import com.xcity.game.adventure.model.ActionTarget;
import com.xcity.game.adventure.model.ActionTargetQueue;
import com.xcity.game.adventure.model.ActionType;
import com.xcity.game.adventure.model.Foe;
import com.xcity.game.adventure.template.AdventureFixedProduceTemplate;
import com.xcity.game.adventure.template.AdventureTemplate;
import com.xcity.game.buildv2.BuildV2;
import com.xcity.game.buildv2.BuildV2.State;
import com.xcity.game.buildv2.BuildV2Part;
import com.xcity.game.common.Cause;
import com.xcity.game.common.GameStaticConfig;
import com.xcity.game.common.ItemResponse;
import com.xcity.game.common.LogCause;
import com.xcity.game.common.MessageConst;
import com.xcity.game.common.PlayerPool;
import com.xcity.game.event.GameEvents;
import com.xcity.game.item.ItemTemplate;
import com.xcity.game.item.drop.listener.ItemDropListener;
import com.xcity.game.item.drop.listener.MultiDropAdapter;
import com.xcity.game.news.NewsService;
import com.xcity.game.player.PlayerUtils;
import com.xcity.game.player.cache.PlayerCacheService;
import com.xcity.game.player.robot.RobotUtils;
import com.xcity.game.relation.RelationService;
import com.xcity.game.relation.Relations;
import com.xcity.pomelo.PomeloRoute;
import com.xcity.pomelo.push.NoticePush;
import com.xcity.pomelo.push.msg.BatchItemsPush;
import com.xcity.pomelo.request.AdventureAttackRequest;
import com.xcity.pomelo.request.AdventureFoesRequest;
import com.xcity.pomelo.request.AdventureInfoRequest;
import com.xcity.pomelo.request.AdventurePlayRequest;
import com.xcity.pomelo.request.AdventureStealRequest;
import com.xcity.pomelo.response.AdventureAttackResponse;
import com.xcity.pomelo.response.AdventureFoesResponse;
import com.xcity.pomelo.response.AdventureInfoResponse;
import com.xcity.pomelo.response.AdventurePlayResponse;
import com.xcity.pomelo.response.AdventureStealResponse;
import com.xcity.pomelo.response.HintResponse;
import com.xcity.pomelo.response.Response;
import com.xcity.util.DBUtils;
import com.xcity.util.SessionUtils;

import gnu.trove.map.TIntObjectMap;
import naga.x.App;
import naga.x.common.Updatable;
import naga.x.event.Event;
import naga.x.event.EventListener;
import naga.x.game.template.TemplateService;
import naga.x.game.time.DayListener;
import naga.x.game.time.TimeUpdater;
import naga.x.net.Packet;
import naga.x.net.Session;
import naga.x.net.annotation.OPHandler;
import naga.x.net.impl.pomelo.PomeloPacket;
import naga.x.net.impl.pomelo.annotation.PomeloOP;
import naga.x.service.Service;
import naga.x.service.ServiceException;
import naga.x.util.CacheListener;
import naga.x.util.CommonUtils;

/**
 * 奇遇v2
 * @author yang.li
 *
 */
@OPHandler
public class AdventureService implements Service, EventListener, CacheListener<Player>, DayListener, Updatable {

	protected List<AdventureTemplate> templates;
	protected TIntObjectMap<AdventureFixedProduceTemplate> fixedProduceTemplates;
	
	protected static final Random RND = new SecureRandom();
	protected static final int COST_STR = 5; // 转一次消耗体力
	
	public static final int ITEM_DOUBLE_REWARD = 40000; // 双倍奖励
	public static final int ITEM_SAFE_BOX = 40002; // 保险箱
	public static final int ITEM_SHIELD = 40003; // 护盾
	public static final int ITEM_AVOID_INVADE = 40004; // 免战牌
//	@Deprecated
//	public static final int STEAL_RATIO_MIN = 5, STEAL_RATIO_MAX = 30; // 偷取比例5%~30%
//	public static final int ATTACK_GAIN_RATIO = 80; // 攻击获得材料比例80%
//	public static final int MIN_STEAL_GOLD = 100000, MAX_STEAL_GOLD = 200000;
	public static final int SHILED_MAX_NUM = 3; // 最多可拥有护盾数量
	
	protected static int doubleRewardPosition; // 双倍奖励所处位置
	
	// 目标队列（每10分钟检测一次队列数量，若不足初始数量1/8，则查询数据库进行补充；每天凌晨清空队列重新补充）
	protected ActionTargetQueue queue;
	// 目标队列初始数量
	protected int queueInitSize;
	protected PlayerCacheService cacheService;
	
	private long tick;
	
	protected static final Logger LOG = LoggerFactory.getLogger(AdventureService.class);
	
	@Override
	public void startup() throws ServiceException {
		cacheService = Objects.requireNonNull(App.getApp().getServiceManager().get(PlayerCacheService.class));
		// 现金、护盾、体力、建材箱、偷窃、攻击
		templates = App.getApp().getServiceManager().get(TemplateService.class).getTemplatesList(AdventureTemplate.class);
		for (int i = 0; i < templates.size(); i++) {
			AdventureTemplate template = templates.get(i);
			// weight arrange
			for (int j = 0; j < AdventureTemplate.WEIGHT_GROUP_SIZE; j++) {
				int weight = i > 0 ? templates.get(i - 1).getWeightTo(j) : 0;
				template.setWeight(j, weight, weight + template.getWeightTo(j));
			}
			
			if (template.getType() == AdventureType.ITEM && template.getData() == ITEM_DOUBLE_REWARD) {
				doubleRewardPosition = i;
			}
		}
		fixedProduceTemplates = App.getApp().getServiceManager().get(TemplateService.class).getTemplates(AdventureFixedProduceTemplate.class);
//		for (int i = 0; i < AdventureTemplate.WEIGHT_GROUP_SIZE; i++) {
//			System.out.println("group " + i + ":");
//			for (AdventureTemplate template : templates) {
//				System.out.print(template.getWeightFrom(i) + "~" + template.getWeightTo(i) + ", ");
//			}
//			System.out.println();
//		}
		
		queue = new ActionTargetQueue();
		initQueue();
		tick = TimeUpdater.getInstance().now();
		
		RobotUtils.getDefault(); // load
		cacheService.addObserver(this);
		App.getApp().getEventManager().register(this);
		TimeUpdater.getInstance().addDayListener(this);
		App.getApp().getUpdater().addAsync(this);
	}
	
	private void initQueue() {
		Object params = DBUtils.wrapParams("minLevel", AdventureUtils.protectionLevel, "maxLevel", PlayerUtils.getExpCalc().getMaxLevel(), "count", queue.capacity());
		List<Long> ids = DBUtils.selectList(PlayerMapper.STATEMENT_FIND_ADVENTURE_TARGET_IDS, params);
		for (int i = ids.size() - 1; i >= 0; i--) {
			Long id = ids.get(i);
			queue.offer(id, false);
		}
		queueInitSize = queue.size();
		LOG.info("[ADVENTURE QUEUE RELOAD]SIZE[{}]CAPACITY[{}]", queueInitSize, queue.capacity());
	}
	
	private void reloadQueue() {
		App.getApp().getThreadPool().execute(new Runnable() {
			@Override
			public void run() {
				initQueue();
			}
		});
	}
	
	@Override
	public void shutdown() throws ServiceException {
	}

	@Override
	public String getId() {
		return getClass().getName();
	}
	
	@Override
	public int[] getEventTypes() {
		return new int[] { GameEvents.EVENT_PLAYER_LOADED, GameEvents.EVENT_PLAYER_LEVEL_UP };
	}

	@Override
	public void handleEvent(Event event) {
		switch (event.getType()) {
			case GameEvents.EVENT_PLAYER_LOADED:
				playerLoaded(event.getParameter(0));
				break;
			case GameEvents.EVENT_PLAYER_LEVEL_UP:
				playerLvlUp(event.getParameter(0), event.getParameter(1));
				break;
		}
	}
	
	private void playerLoaded(Player player) {
		cacheAdded(player);
		player.getAdventure().validate(this);
	}
	
	private void playerLvlUp(Player player, int oldLvl) {
		cacheAdded(player);
	}
	
	@Override
	public boolean dayChanged() {
		queue.clear();
		reloadQueue();
		return true;
	}
	
	@Override
	public boolean update() {
		long now = TimeUpdater.getInstance().now();
		if (now - tick > 600000) {
			tick = now;
			if (queueInitSize > 8 && queue.size() < (queueInitSize >> 3)) {
				// 队列数量不足初始数量1/8，则查询数据库进行补充
				reloadQueue();
			}
		}
		return true;
	}

	@PomeloOP(PomeloRoute.ADVENTURE_INFO)
	protected void requestInfo(Session session, AdventureInfoRequest req) {
		Player player = session.getClient();
		if (player.getAdventure().getNextStealTarget() == null) {
			player.getAdventure().setNextStealTarget(findNextStealTarget(player));
		}
		session.send(new AdventureInfoResponse(req.getSerial(), player, COST_STR, templates));
	}
	
	@PomeloOP(PomeloRoute.ADVENTURE_PLAY)
	protected void requestPlay(Session session, AdventurePlayRequest req) {
		Player player = session.getClient();
		AdventureType t = player.getAdventure().getLastAction();
		if (t == AdventureType.ATTACK || t == AdventureType.STEAL || t == AdventureType.CAPTURE) {
			session.send(new HintResponse(req.getSerial(), MessageConst.OPERATE_TOO_FREQUENT));
			return;
		}
		if (!player.decStrength(COST_STR, Cause.ADVENTURE)) {
			session.send(new HintResponse(req.getSerial(), MessageConst.STR_NOT_ENOUGH));
			return;
		}
		int pos = indexOfRandomly(player);
		boolean isDouble = player.getPool().getIntValue(PlayerPool.PROPERTY_ADVENTURE_POSITION) == doubleRewardPosition;
		Packet packet;
		while ((packet = doPlay(req, player, pos, isDouble)) == null) {
			// pos = 2 11 19
			int n;
			pos = (n = RND.nextInt(3)) == 0 ? 2 : (n == 1 ? 11 : 19); // gold=15000
		}
		player.poolAdd(PlayerPool.PROPERTY_ADVENTURE_STEPS, 1);
		player.addExp(GameStaticConfig.adventureExp, Cause.ADVENTURE);
		session.send(packet);
		LOG.info("[ADVENTURE]ID[{}]POS[{}]", player.getId(), pos);
		App.getApp().getEventManager().addEvent(GameEvents.EVENT_ADVENTURE_PLAY, player);
	}
	
	private int indexOfRandomly(Player player) {
		if (AdventureUtils.getAdventureCaptureFuncCount(player) == 0) { // 刚开启占领功能，必转到
			return GameStaticConfig.adventureCaptureFuncPos;
		}
		int steps = player.getPool().getIntValue(PlayerPool.PROPERTY_ADVENTURE_STEPS);
		AdventureFixedProduceTemplate afpt = fixedProduceTemplates.get(steps + 1);
		if (afpt != null) {
			return afpt.getGrid() - 1;
		}
		int i = (player.getBuildings2().size() - 1) / 3; // 每多3个建筑，概率改变一次
		if (i >= AdventureTemplate.WEIGHT_GROUP_SIZE) {
			i = AdventureTemplate.WEIGHT_GROUP_SIZE - 1;
		}
		
		int weight = RND.nextInt(templates.get(templates.size() - 1).getWeightTo(i)/*the max weight*/);
		int start = 0, end = templates.size() - 1;
		while (start <= end) {
			int mid = (start + end) >> 1;
			AdventureTemplate t = templates.get(mid);
			if (weight < t.getWeightFrom(i)) {
				end = mid - 1;
				continue;
			}
			if (weight >= t.getWeightTo(i)) {
				start = mid + 1;
				continue;
			}
			return mid;
		}
		return -1;
	}
	
	private AdventurePlayResponse doPlay(AdventurePlayRequest req, Player player, int pos, boolean isDouble) {
		player.poolSet(PlayerPool.PROPERTY_ADVENTURE_POSITION, pos);
		AdventureTemplate template;
		int capFuncCount = -1;
		if (pos == GameStaticConfig.adventureCaptureFuncPos
					&& (capFuncCount = AdventureUtils.getAdventureCaptureFuncCount(player)) >= 0) {
			template = AdventureTemplate.capture;
		} else {
			template = templates.get(pos);
		}
		AdventureType type = template.getType();
		switch (type) {
			case GOLD:
				player.addGold(isDouble ? template.getData() * 2 : template.getData(), LogCause.ADVENTURE);
				break;
			case STRENGTH:
				player.addStrength(isDouble ? template.getData() * 2 : template.getData(), LogCause.ADVENTURE);
				break;
			case ITEM:
				processItem(player, template, isDouble);
				break;
//			case STEAL:
//				processSteal(player, isDouble);
//				break;
//			case ATTACK:
//				processAttack(player, isDouble);
//				break;
			default:
				break;
		}
		
		player.getAdventure().setLastAction(type);
		player.getAdventure().setDoubled(isDouble);
		
		if (type == AdventureType.STEAL) {
			List<ActionTarget> stealTargets = fillStealTargets(player, player.getAdventure().getNextStealTarget());
			if (stealTargets == null) {
				return null;
			}
			player.getAdventure().setLastActionTargets(stealTargets);
			return new AdventurePlayResponse(req.getSerial(), pos, stealTargets);
		} else if (type == AdventureType.ATTACK) {
			ActionTarget attackTarget = findAttackTarget(player);
			if (attackTarget == null) {
				return null;
			}
			player.getAdventure().setLastActionTargets(Arrays.asList(attackTarget));
			return new AdventurePlayResponse(req.getSerial(), pos, attackTarget);
		} else if (type == AdventureType.CAPTURE) {
			ActionTarget captureTarget = findCaptureTarget(player, capFuncCount);
			if (captureTarget == null) {
				return null;
			}
			player.getAdventure().setLastActionTargets(Arrays.asList(captureTarget));
			AdventureUtils.addAdventureCaptureFuncCount(player, 1);
			return new AdventurePlayResponse(req.getSerial(), pos, type, null, captureTarget);
		} else {
			return new AdventurePlayResponse(req.getSerial(), pos, type);
		}
	}
	
	private void processItem(Player player, AdventureTemplate template, boolean isDouble) {
		int itemId = template.getData();
		if (itemId == ITEM_DOUBLE_REWARD) { // 双倍奖励，不用放入背包
			return;
		}
//		if (itemId == ITEM_SAFE_BOX) { // 保险箱
//			return;
//		}
		if (itemId == ITEM_SHIELD) { // 护盾
			int c = isDouble ? 2 : 1;
			while (c-- > 0) {
				if (player.getShield() < SHILED_MAX_NUM) {
//					player.getBag().addItemWithoutException(itemId, 1, Cause.ADVENTURE);
					player.addShield(1, LogCause.ADVENTURE);
					// 成就特殊处理
					player.getAchievements().fireUseItem(itemId, 1);
				} else { // 护盾满则转为体力
					player.addStrength(COST_STR, LogCause.SHIELD_FULL);
				}
			}
			return;
		}
//		if (itemId == ITEM_AVOID_INVADE) { // 免战牌
//			return;
//		}
		
		final BatchItemsPush sync = new BatchItemsPush(2);
		ItemDropListener lsr = new MultiDropAdapter() {
			@Override
			public void itemDroped(Player player, int itemId, int count) {
				sync.add(itemId, count);
			}
		};
		player.getBag().addItemWithoutException(itemId, isDouble ? 2 : 1, Cause.ADVENTURE, lsr);
		if (!sync.isEmpty()) {
			player.send(sync);
		}
	}
	
	@PomeloOP(PomeloRoute.ADVENTURE_STEAL)
	protected void requestSteal(Session session, AdventureStealRequest req) {
		Player player = session.getClient();
		if (player.getAdventure().getLastAction() != AdventureType.STEAL) {
//			session.send(new HintResponse(req.getSerial(), MessageConst.ILLEGAL_OPERATION));
			session.send(getStealErrorResponse(session, req));
			return;
		}
		List<ActionTarget> targets = player.getAdventure().getLastActionTargets();
		if (targets == null || targets.isEmpty()) {
//			session.send(new HintResponse(req.getSerial(), MessageConst.SYSTEM_ERROR));
			session.send(getStealErrorResponse(session, req));
			return;
		}
		int index = req.getIndex();
		if (index < 0 || index >= targets.size()) {
//			session.send(new HintResponse(req.getSerial(), MessageConst.ILLEGAL_OPERATION));
			session.send(getStealErrorResponse(session, req));
			return;
		}
		player.getAdventure().setLastAction(null);
		player.getAdventure().setLastActionTargets(null);
		int multiply = player.getAdventure().isDoubled() ? 2 : 1; // 检测是否双倍奖励
		player.getAdventure().setDoubled(false);
		
		PlayerCacheService service = this.cacheService;
		ActionTarget rightTarget = null;
		long loseGold = 0;
		Player targetPlayer = null;
		List<AdventureStealResponse.Result> result = new ArrayList<>(3);
		for (int i = 0; i < targets.size(); i++) {
			ActionTarget t = targets.get(i);
			Player p = service.find(t.getId());
			if (i == index) { // 玩家选择的目标
				targetPlayer = p;
				player.addGold(t.getGold() * multiply, Cause.ADVENTURE_STEAL);
				if (!RobotUtils.isRobot(p)) {
					loseGold = t.getGold() * multiply;
					if (p.getPool().getIntValue(PlayerPool.PROPERTY_SAFE_BOX) > 0) { // 保险箱
						p.poolSubtract(PlayerPool.PROPERTY_SAFE_BOX, 1);
						ItemTemplate item = App.getApp().getServiceManager().get(TemplateService.class).get(ItemTemplate.class, ITEM_SAFE_BOX);
						loseGold -= loseGold * item.getParameter(0) / GameStaticConfig.percentBase;
					}
					if (loseGold > p.getGold()) { // 修正被偷金额
						loseGold = p.getGold();
					}
					p.costGold(loseGold, Cause.ADVENTURE_STEAL);
					p.getAdventure().addFoe(new Foe(player.getId(), t.getGold()));
					if (loseGold > 0) {
						App.getApp().getServiceManager().get(NewsService.class).send(
								p,
								MessageConst.ADVENTURE_EVENT_MAIL_TITLE,
								MessageFormat.format(MessageConst.ADVENTURE_BE_STOLEN_EVENT, player.getName(), player.getId(), loseGold));
					}
				}
			}
			if (rightTarget == null || t.getGold() > rightTarget.getGold()) {
				rightTarget = t;
			}
			result.add(new AdventureStealResponse.Result(p, t.getGold()));
		}
		
		ActionTarget target = targets.get(index);
		boolean bingo = rightTarget.getId() == target.getId();
		AdventureStealResponse res;
		if (bingo) { // 猜中目标后需更新下一次偷取目标
			player.getAdventure().setNextStealTarget(findNextStealTarget(player));
			res = new AdventureStealResponse(req.getSerial(), index, true, result, player.getAdventure().getNextStealTarget());
		} else {
			res = new AdventureStealResponse(req.getSerial(), index, false, result);
		}
		session.send(res);
		session.setAttribute(SessionUtils.ADVENTURE_STEAL_RESPONSE, new SoftReference<AdventureStealResponse>(res));
		App.getApp().getEventManager().addEvent(GameEvents.EVENT_ADVENTURE_STEAL, player, target.getGold() * multiply, bingo, targetPlayer, loseGold);
		LOG.info("[ADVENTURE STEAL]ID[{}]TARGET[{}]GOLD[{}]MUL[{}]RIGHT[{}]", player.getId(), target.getId(), target.getGold(), multiply, rightTarget.getId() == target.getId() ? 1 : 0);
	}
	
	private static Response getStealErrorResponse(Session session, PomeloPacket req) {
		SoftReference<AdventureStealResponse> ref = session.getAttribute(SessionUtils.ADVENTURE_STEAL_RESPONSE);
		Response res = ref != null ? ref.get() : null;
		if (res != null) {
			res.setSerial(req.getSerial());
			return res;
		}
		return new HintResponse(req.getSerial(), MessageConst.ILLEGAL_OPERATION);
	}
	
	private List<ActionTarget> fillStealTargets(Player source, ActionTarget rightTarget) {
		List<ActionTarget> list = new ArrayList<ActionTarget>(3);
		list.add(rightTarget);
		int retrys = 0;
		while (retrys++ < 10) {
			ActionTarget target = ActionType.STEAL.find(queue, source);
			if (target != null && !list.contains(target)) {
				list.add(target);
			}
			if (list.size() == 3) {
				break;
			}
		}
		if (list.size() < 3) {
			return null;
		}
		AdventureUtils.fixStealsGold(list);
		CommonUtils.shuffle(list);
		return list;
	}
	
	public ActionTarget findNextStealTarget(Player source) {
//		if (source.getPool().getIntValue(PlayerPool.PROPERTY_ADVENTURE_STEPS) == 0) {
//			return RobotUtils.getDefaultStealTarget(source);
//		}
//		if (IdUtils.sizeOfPlayer() < 100) {
//			return RobotUtils.findStealTarget(source);
//		}
//		ActionTarget target = actionTargetGroup.matchStealTarget(source, 0);
//		if (target != null) {
//			fixStealTargetGold0(target);
//			return target;
//		}
//		return RobotUtils.findStealTarget(source);
		
		ActionTarget target = ActionType.STEAL.find(queue, source);
		AdventureUtils.fixStealTargetGold(target);
		return target;
	}
	
	//---策划们为了体验，加一堆调整 BEGIN---
//	private static void fixStealTargetGold0(ActionTarget target) {
//		if (target.getGold() < 100000) {
//			target.setGold(target.getGold() + target.getGold() / 2);
//		}
//	}
//	
//	private static void fixStealTargetGold1(ActionTarget target) {
//		int val = (int) (target.getGold() - target.getGold() * 0.5f);
//		if (val > 0) {
//			target.setGold(val);
//		}
//	}
//	
//	private static void fixStealTargetGold2(ActionTarget target) {
//		int val = (int) (target.getGold() - target.getGold() * 0.7f);
//		if (val > 0) {
//			target.setGold(val);
//		}
//	}
	//---策划们为了体验，加一堆调整 END---
	
	private ActionTarget findAttackTarget(Player source) {
//		if (source.getPool().getIntValue(PlayerPool.PROPERTY_ADVENTURE_STEPS) == 1) { // 新手引导抢机器人
//			return new ActionTarget(RobotUtils.getDefault().getId());
//		}
//		if (IdUtils.sizeOfPlayer() > 100) {
//			ActionTarget target = actionTargetGroup.matchAttackTarget(source);
//			if (target != null) {
//				return target;
//			}
//		}
//		return RobotUtils.findStealTarget(source);
		return ActionType.ATTACK.find(queue, source);
	}
	
	private ActionTarget findCaptureTarget(Player source, int count) {
//		if (count == 0) { // 第一次固定目标为机器人
//			return new ActionTarget(GameStaticConfig.adventureFirstCaptureRobotId);
//		}
//		if (IdUtils.sizeOfPlayer() > 100) {
//			ActionTarget target = actionTargetGroup.matchCaptureTarget(source);
//			return target;
//		}
//		return null;
		return ActionType.CAPTURE.find(queue, source);
	}
	
	@PomeloOP(PomeloRoute.ADVENTURE_ATTACK)
	protected void requestAttack(Session session, AdventureAttackRequest req) {
		int buildPos = req.getBuildPart();
		if (buildPos < 0 || buildPos >= BuildV2.PART_COUNT) {
//			session.send(new HintResponse(req.getSerial(), MessageConst.ILLEGAL_OPERATION));
			session.send(new AdventureAttackResponse(req.getSerial(), false, null));
			return;
		}
		Player player = session.getClient();
		if (player.getAdventure().getLastAction() != AdventureType.ATTACK) {
//			session.send(new HintResponse(req.getSerial(), MessageConst.ILLEGAL_OPERATION));
			session.send(new AdventureAttackResponse(req.getSerial(), false, null));
			return;
		}
		List<ActionTarget> targets = player.getAdventure().getLastActionTargets();
		if (targets == null || targets.isEmpty()) {
//			session.send(new HintResponse(req.getSerial(), MessageConst.SYSTEM_ERROR));
			session.send(new AdventureAttackResponse(req.getSerial(), false, null));
			return;
		}
		long id = 0L;
		boolean isFoe = false; // 攻击目标是否为仇人
		if (req.getId() == targets.get(0).getId()) {
			id = req.getId();
		} else {
			// 如果不是系统随机的目标，则查找仇人列表
			List<Foe> foes = player.getAdventure().getFoes();
			for (int i = 0, size = foes != null ? foes.size() : 0; i < size; i++) {
				Foe foe = foes.get(i);
				if (req.getId() == foe.getId()) {
					id = req.getId();
					isFoe = true;
					break;
				}
			}
		}
		if (id == 0L) {
			// 不是随机目标或仇人，则查找好友列表
			boolean isFriend = App.getApp().getServiceManager().get(RelationService.class).isFriend(player.getId(), req.getId());
			if (!isFriend) {
//				session.send(new HintResponse(req.getSerial(), MessageConst.ILLEGAL_OPERATION));
				session.send(new AdventureAttackResponse(req.getSerial(), false, null));
				return;
			}
			id = req.getId();
		}
		Player target = cacheService.find(id);//App.getApp().getServiceManager().get(ActorService.class).find(id);
		if (target == null) {
//			session.send(new HintResponse(req.getSerial(), MessageConst.PLAYER_NOT_EXIST));
			session.send(new AdventureAttackResponse(req.getSerial(), false, null));
			return;
		}
		BuildV2 build = target.getBuildings2().find(req.getBuildId());
		if (build == null) {
//			session.send(new HintResponse(req.getSerial(), MessageConst.TARGET_PLAYER_BUILD_NOT_EXIST));
			session.send(new AdventureAttackResponse(req.getSerial(), false, null));
			return;
		}
		if (build.getState() != State.BUILDING) {
//			session.send(new HintResponse(req.getSerial(), MessageConst.ADVENTURE_ATTACK_FINISHED_BUILD));
			session.send(new AdventureAttackResponse(req.getSerial(), false, null));
			return;
		}
		BuildV2Part buildPart = build.getPart(buildPos);
		if (buildPart.getLevel() <= 0) {
//			session.send(new HintResponse(req.getSerial(), MessageConst.ADVENTURE_ATTACK_NOT_BUILD_PART));
			session.send(new AdventureAttackResponse(req.getSerial(), false, null));
			return;
		}
		
		player.getAdventure().setLastAction(null);
		player.getAdventure().setLastActionTargets(null);
		if (isFoe) {
			player.getAdventure().removeFoe(target.getId());
		}
		int multiply = player.getAdventure().isDoubled() ? 2 : 1; // 检测是否双倍奖励
		player.getAdventure().setDoubled(false);
		
		boolean isHit = target.getShield() < 1;
		ItemTemplate item = buildPart.template.getItem();
		if (!isHit) { // 护盾
			target.decShield(1, LogCause.ADVENTURE_ATTACK);
			target.getAdventure().addFoe(new Foe(player.getId(), item.getId(), 0));
			// 邮件、新闻
			App.getApp().getServiceManager().get(NewsService.class).send(
					target,
					MessageConst.ADVENTURE_EVENT_MAIL_TITLE,
					MessageFormat.format(
							MessageConst.ADVENTURE_BE_ATTACKED_EVENT_WITH_SHIELD,
							player.getName(),
							player.getId(),
							build.getTemplate().getName()
					)
			);
			// 跑马灯公告
			if (target.isOnline()) {
				target.send(new NoticePush(
						NoticePush.NOTICE_MARQUEE,
						MessageFormat.format(
								MessageConst.ADVENTURE_BE_ATTACKED_EVENT_WITH_SHIELD_NOTICE,
								player.getName(),
								build.getTemplate().getName()
						)
						)
				);
			}
			session.send(new AdventureAttackResponse(req.getSerial(), false, null));
			LOG.info("[ADVENTURE ATTACK]ID[{}]TARGET[{}]BUILD[{}]PART[{}]HIT[0]MUL[{}]", player.getId(), target.getId(), build.getId(), buildPart.getIndex(), multiply);
		} else {
			// 固定奖励30个
			int itemCount = 30 * multiply;//(int) (buildPart.template.getItemCount(buildPart.getLevel()) * ATTACK_GAIN_RATIO / GameStaticConfig.percentBase);
//			if (itemCount <= 0) {
//				itemCount = 1;
//			}
			player.getBag().addItemWithoutException(item, itemCount, Cause.ADVENTURE_ATTACK);
			if (!RobotUtils.isRobot(target)) {
				build.levelDown(buildPos, Cause.ADVENTURE_ATTACK);
				target.getAdventure().addFoe(new Foe(player.getId(), item.getId(), itemCount));
				// 邮件、新闻
				App.getApp().getServiceManager().get(NewsService.class).send(
						target,
						MessageConst.ADVENTURE_EVENT_MAIL_TITLE,
						MessageFormat.format(
								MessageConst.ADVENTURE_BE_ATTACKED_EVENT,
								build.getTemplate().getName(),
								player.getName(),
								player.getId(),
								buildPart.getTemplate().name
						)
				);
				// 跑马灯公告
				if (target.isOnline()) {
					target.send(new NoticePush(
							NoticePush.NOTICE_MARQUEE,
							MessageFormat.format(
									MessageConst.ADVENTURE_BE_ATTACKED_EVENT_NOTICE,
									build.getTemplate().getName(),
									player.getName(),
									buildPart.getTemplate().name
									)
							)
					);
				}
			}
			session.send(new AdventureAttackResponse(req.getSerial(), true, new ItemResponse(item.getId(), itemCount)));
			LOG.info("[ADVENTURE ATTACK]ID[{}]TARGET[{}]BUILD[{}]PART[{}]HIT[1]MUL[{}]", player.getId(), target.getId(), build.getId(), buildPart.getIndex(), multiply);
		}
//		if (target instanceof Actor) {
//			((Actor) target).setDirty(true);
//		}
		App.getApp().getEventManager().addEvent(GameEvents.EVENT_ADVENTURE_ATTACK, player, target, build, buildPart, isHit);
	}
	
	@PomeloOP(PomeloRoute.ADVENTURE_FOES)
	protected void requestFoes(Session session, AdventureFoesRequest req) {
		Player player = session.getClient();
		if (req.type == 0) {
			// 仇人列表
			ActionTarget attackTarget = player.getAdventure().getAttackTarget(); // 攻击目标
			session.send(new AdventureFoesResponse(req.getSerial(), player.getAdventure().getFoes(), attackTarget));
		} else {
			// 好友列表
			Relations rels = App.getApp().getServiceManager().get(RelationService.class).findRelations(player);
//			List<Relation2> friendRels = rels.getRelations(Relation2.TYPE_FRIEND);
//			int size = friendRels.size();
			int size = rels.sizeOf(Relation2.TYPE_FRIEND);
			if (size == 0) {
				session.send(new AdventureFoesResponse(req.getSerial(), (byte) 1));
				return;
			}
			final List<Player> friends = new ArrayList<Player>(size);
			final PlayerCacheService pcs = App.getApp().getServiceManager().get(PlayerCacheService.class);
			rels.forEach(Relation2.TYPE_FRIEND, new Consumer<Relation2>() {
				@Override
				public void accept(Relation2 rel) {
					Player friend = pcs.find(rels.getDestId(rel));
					if (friend != null && friend.getBuildings2().getLast().canBeAttacked()) {
						friends.add(friend);
					}
				}
			});
			session.send(new AdventureFoesResponse(req.getSerial(), (byte) 1, friends));
		}
	}

	@Override
	public void cacheAdded(Player player) {
		if (!AdventureUtils.inProtection(player, queue)) {
			queue.offer(player);
		}
	}

	@Override
	public void cacheRemoved(Player player) {
	}

	@Override
	public void cacheEldestRemoved(Player player) {
	}

}
